package io.github.bigbird0101.easycluster.server;


import io.github.bigbird0101.easycluster.URL;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultThreadFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.ThreadFactory;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

public class NettyServer extends AbstractServer{
    static String NETTY_EPOLL_ENABLE_KEY = "netty.epoll.enable";
    static String OS_NAME_KEY = "os.name";
    static String OS_LINUX_PREFIX = "linux";
    int DEFAULT_HEARTBEAT = 60 * 1000;

    int DEFAULT_IO_THREADS = Math.min(Runtime.getRuntime().availableProcessors() + 1, 32);
    private ServerBootstrap bootstrapServer;

    public NettyServer(URL url) {
        super(url);
    }

    public void doStart(){
        bootstrapServer = new ServerBootstrap();
        bootstrapServer.group(createEventLoopGroup(1),createEventLoopGroup(DEFAULT_IO_THREADS));
        bootstrapServer.channel(createChannel());
        bootstrapServer
                .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                .childOption(ChannelOption.SO_KEEPALIVE, false)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        bootstrapServer.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) {
                ch.pipeline()
                        .addLast(new StringDecoder())
                        .addLast(new StringEncoder())
                        .addLast("server-idle-handler", new IdleStateHandler(0, 0, DEFAULT_HEARTBEAT*3, MILLISECONDS))
                        .addLast(new SimpleChannelInboundHandler<Object>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
                                System.out.println(msg);
                                ctx.channel().writeAndFlush(msg);
                            }
                        });
            }
        });
    }

    public void doBind(){
       bootstrapServer.bind(new InetSocketAddress(getUrl().getHost(),getUrl().getPort())).syncUninterruptibly();
       System.out.println("启动成功");
    }

    private EventLoopGroup createEventLoopGroup(int nums) {
        ThreadFactory threadFactory=new DefaultThreadFactory("NettyClient",true);
        return shouldEpoll()?new EpollEventLoopGroup(nums,threadFactory):new NioEventLoopGroup(DEFAULT_IO_THREADS,threadFactory);
    }

    private Class<? extends ServerChannel> createChannel() {
        return shouldEpoll()? EpollServerSocketChannel.class : NioServerSocketChannel.class;
    }

    private static boolean shouldEpoll() {
        if (Boolean.parseBoolean(System.getProperty(NETTY_EPOLL_ENABLE_KEY, "false"))) {
            String osName = System.getProperty(OS_NAME_KEY);
            return osName.toLowerCase().contains(OS_LINUX_PREFIX) && Epoll.isAvailable();
        }
        return false;
    }
}